package cn.edu.zju.qsc.rouchseg.segment;

import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeMap;

import cn.edu.zju.qsc.rouchseg.tools.FileTool;
import cn.edu.zju.qsc.rouchseg.tools.StrTool;

public class Seg {
	private boolean reverse;	/** 是否逆向匹配 */
	
	private List<String> allDicWords;
	private Map dicMap;
	
	private List<String> result;
	private Integer singleWordNum;	/** 单字成词的个数 */
	private Integer longWordNum;	/** 长词个数 */
	private final int longWordNumStandard;	/** >=多少就算长词 */
	
	public Seg() {
		this.reverse = false;
		this.singleWordNum = 0;
		this.longWordNum = 0;
		this.longWordNumStandard = 3;
	}
	
	public Seg(int longWordNumStandard) {
		this.reverse = false;
		this.singleWordNum = 0;
		this.longWordNumStandard = longWordNumStandard;
	}
	
	public void setDicMap(Map dic) {
		this.dicMap = dic;
	}
	
	public void setReverse(boolean reverse) {
		this.reverse = reverse;
	}
	
	private void clearSingleWordNum() {
		this.singleWordNum = 0;
	}
	public Integer getSingleWordNum() {
		return this.singleWordNum;
	}
	
	private void clearLongWordNum() {
		this.longWordNum = 0;
	}
	public Integer getLongWordNum() {
		return this.longWordNum;
	}
	
	public List<String> getResult() {
		return this.result;
	}
	
	/** 读字典文件 */
	public void readDicFile(String filename) throws FileNotFoundException, UnsupportedEncodingException {
		this.allDicWords = FileTool.readDicFile(filename);
	} //end function readDicFile
	
	/** 加载字典入内存 */
	public Map loadDic(List<String> words) {
		if (words != null) {
			this.allDicWords = words;
		}
		if (this.allDicWords == null) {
			return null;
		}
		/** 首字-->词组长度为索引的Map */
		Map<Character, Map> firstCharMap = new TreeMap<Character, Map>();
		/** 词组长度-->尾字为索引的Map */
		Map<Integer, Map> charLengthMap = null;		//new TreeMap<Integer, Map>();
		/** 尾字-->词组组成的List */
		Map<Character, List> tailCharMap = null;	//new TreeMap<Character, List>();
		/** 词组的List */
		List<String> dicWords = null;				//new ArrayList<String>();
		
		String word;
		for (int i = 0; i < this.allDicWords.size(); i++) {
			word = this.allDicWords.get(i);
			if (word.isEmpty() == true) {
				continue;
			}
			if (this.reverse == true) {
				word = StrTool.reverse(word);
			}
			/** 如果是第一次出现以这个字开头的词语 */
			if (firstCharMap.containsKey(word.charAt(0)) == false) {
				dicWords = new ArrayList<String>();
				tailCharMap = new TreeMap<Character, List>();
				charLengthMap = new TreeMap<Integer, Map>();
				
				dicWords.add(word);
				tailCharMap.put(word.charAt(word.length()-1), dicWords);
				charLengthMap.put(word.length(), tailCharMap);
				firstCharMap.put(word.charAt(0), charLengthMap);
			} else {
				charLengthMap = firstCharMap.get(word.charAt(0));
				/** 如果由该特定词头开始的词是第一次出现该长度 */
				if (charLengthMap.containsKey(word.length()) == false) {
					dicWords = new ArrayList<String>();
					tailCharMap = new TreeMap<Character, List>();
					
					dicWords.add(word);
					tailCharMap.put(word.charAt(word.length()-1), dicWords);
					charLengthMap.put(word.length(), tailCharMap);
				} else {
					tailCharMap = charLengthMap.get(word.length());
					/** 如果该特定词头开始、长度为该特定长度的词是第一次以该字结尾 */
					if (tailCharMap.containsKey(word.charAt(word.length()-1)) == false) {
						dicWords = new ArrayList<String>();
						
						dicWords.add(word);
						tailCharMap.put(word.charAt(word.length()-1), dicWords);
					} else {
						dicWords = tailCharMap.get(word.charAt(word.length()-1));
						dicWords.add(word);
					}
				}
			}
				
		} //end for
		this.dicMap = firstCharMap;
		/** 以后要删除 */
		return firstCharMap;
	} //end function loadDic
	
	/** 分词算法主体 */
	public void doSeg(String sentence) {
		/** 最终结果 */
		List<String> words = new ArrayList<String>();
		
		/** 标注是否找到 */
		boolean found = false;
		
		Character firstWord = null;	/** 首字 */
		Character lastWord = null;	/** 尾字 */
		String splittedWord = null; /** 整词 */
		
		/** 词组长度-->尾字为索引的Map */
		Map<Integer, Map> charLengthMap = null;
		/** 尾字-->词组组成的List */
		Map<Character, List> tailCharMap = null;
		/** 词组的List */
		List<String> dicWords = null;
		/** 词组长度的set */
		SortedSet<Integer> lengthSet = null;
		
		/** 词组长度 */
		Integer length;
		/** 切分出来准备装入List返回的词 */
		String wordReturn;
		
		/** 初始化，准备开始分词 */
		clearSingleWordNum();
		clearLongWordNum();
		if (this.reverse == true) {
			sentence = StrTool.reverse(sentence);
		}
		firstWord = sentence.charAt(0);
		/** 循环直到找到一个字典中有的字 */
		while (sentence.isEmpty() == false && this.dicMap.containsKey(firstWord) == false) {
			/** 单字成词 */
			words.add(new String(sentence.substring(0, 1)));
			++this.singleWordNum; /** 增加单字成词的个数 */
			sentence = sentence.substring(1);
			if (sentence.isEmpty() == false) {
				firstWord = sentence.charAt(0);
			}
		} //end while
		if (this.dicMap.containsKey(firstWord)) {
			charLengthMap = (Map<Integer, Map>) this.dicMap.get(firstWord);
			lengthSet = (SortedSet<Integer>) charLengthMap.keySet();
		}
		while (lengthSet != null && lengthSet.isEmpty() == false) {

			length = lengthSet.last();
			/** 保证待分词的句子长度 >= 字典中的词长 */
			if (sentence.length() >= length) {
				tailCharMap = charLengthMap.get(length);	
				lastWord = sentence.charAt(length-1);	/** 该长度词的最后一个字 */
				/** 检查字典中是不是有以firstWord开头，长度为length，lastWord结尾的词 */
				if (tailCharMap.containsKey(lastWord)) {
					/** 再检查整词是否符合 */
					splittedWord = sentence.substring(0, length);
					dicWords = tailCharMap.get(lastWord);
					/** 找到一个匹配的词 */
					if (dicWords.contains(splittedWord)) {
						found = true;
					}
				} 
			}
			
			/** 如果字典中没有更短的词，就表明单字成词 */
			if (found == false && lengthSet.headSet(lengthSet.last()).isEmpty() == true) {
				length = 1;
				found = true;
				++this.singleWordNum;
			}
			/** 为下一轮循环准备 */
			if (found == true) {
				while (found == true) {
					/** 对长词个数加一 */
					if (length >= this.longWordNumStandard) {
						++this.longWordNum;
					}
					wordReturn = new String(sentence.substring(0, length));
					if (this.reverse == true) {
						wordReturn = StrTool.reverse(wordReturn);
					}
					words.add(wordReturn);
					
					/** 截取sentence */
					sentence = sentence.substring(length);
					
					if (sentence.isEmpty() == false) {
						firstWord = sentence.charAt(0);
						if (this.dicMap.containsKey(firstWord)) {
							charLengthMap = (Map<Integer, Map>) this.dicMap.get(firstWord);
							lengthSet = (SortedSet<Integer>) charLengthMap.keySet();
							
							found = false;
						} else {
							/** 单字成词 */
							length = 1;
						}
					} else {
						break;	/** 结束 */
					}
				} // end inner while
				if (sentence.isEmpty() == true) {
					break;	/** 分词结束 */
				}
			} else {
				/** 重取一个Set，删除最大的值 */
				lengthSet = lengthSet.headSet(lengthSet.last());
			}
		} //end while
		
		this.result = words;
	}
	
	/** 把分词结果中连续的单个字合并起来 */
	public void combineResult(boolean subSingleWordNum) {
		List<String> combinedResult = new ArrayList<String>();
		StringBuilder str;
		
		if (subSingleWordNum) {
			clearSingleWordNum();	/** 清空单字成词的个数 */
		}
		int i = 0;
		while (i < this.result.size()) {
			str = new StringBuilder(this.result.get(i));
			if (this.result.get(i).length() == 1) {
				while (i < (this.result.size()-1) && this.result.get(i+1).length() == 1) {
					if (this.reverse) {
						str.insert(0, this.result.get(i+1));
					} else {
						str.append(this.result.get(i+1));
					}
					++i;
				}
			}
			++i;
			combinedResult.add(str.toString());	
			/** 更新单字成词的个数 */
			if (combinedResult.get(combinedResult.size()-1).length() == 1 
					&& subSingleWordNum) {
				++this.singleWordNum;
			}
		}
		this.result = combinedResult;
	}
}
